﻿using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Puppy.Scheduler.Processors
{
    /// <summary>
    /// 脚本处理器基类
    /// </summary>
    public abstract class ScriptProcessorBase : ProcessorBase
    {
        protected virtual List<string> DownloadProtocol { get; set; } = new List<string> { "http", "https", "ftp" };

        protected override async Task<ProcessResult> ExecuteAsync(TaskContext context, CancellationToken cancellationToken = default)
        {
            Logger.LogInformation("start to process ScriptProcessor");

            var script = await PrepareScriptFile(context.InstanceId, context.ParseParams());

            Process process = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = GetRunCommand(),
                    UseShellExecute = false,
                    CreateNoWindow = true,
                    RedirectStandardError = true,
                    RedirectStandardInput = true,
                    RedirectStandardOutput = true
                }
            };

            StringBuilder outputBuilder = new StringBuilder();
            StringBuilder errorBuilder = new StringBuilder();
            string result;

            try
            {
                process.Start();
                await process.StandardInput.WriteLineAsync(script);
                process.StandardInput.AutoFlush = true;

                await CopyStream(process.StandardOutput, outputBuilder);
                await CopyStream(process.StandardError, errorBuilder);

                await process.WaitForExitAsync(cancellationToken);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "ScriptProcessor has been interrupted");
            }
            finally
            {
                var error = errorBuilder.ToString();
                if (!error.IsNullOrWhiteSpace())
                    outputBuilder.AppendLine($"[Error]: {error}");
                result = $"[Output]: {outputBuilder}";
            }

            return new ProcessResult(true, result);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="instanceId"></param>
        /// <param name="processorInfo"></param>
        /// <returns></returns>
        /// <exception cref="IOException"></exception>
        private async Task<string> PrepareScriptFile(long instanceId, string processorInfo)
        {
            string script = processorInfo;

            // 如果是下载链接，则从网络获取
            foreach (var protocol in DownloadProtocol)
            {
                if (processorInfo.StartsWith(protocol))
                {
                    // todo: 读取网络文本
                    script = "";
                    break;
                }
            }

            // 命令末尾需要添加退出指令，否则当调用ReadToEnd()方法时，会处于假死状态 
            script = script.Replace("\"", "\\\"");
            if (!script.Contains("exit", StringComparison.OrdinalIgnoreCase))
            {
                script = script + Environment.NewLine + "exit";
            }

            return script;
        }

        private async Task CopyStream(StreamReader streamReader, StringBuilder stringBuilder)
        {
            try
            {
                while (!streamReader.EndOfStream)
                {
                    var line = await streamReader.ReadLineAsync();
                    stringBuilder.AppendLine(line);
                    // todo: 同步到在线日志
                    Logger.LogInformation(line);
                }
            }
            catch (Exception ex)
            {
                Logger.LogWarning(ex, "CopyStream failed.");
                stringBuilder.AppendLine($"Exception: {ex.Message}");
            }
        }

        /// <summary>
        /// 获取运行命令（eg，shell返回 /bin/sh）
        /// </summary>
        /// <returns>执行脚本的命令</returns>
        protected abstract string GetRunCommand();
    }
}
